{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 一、验证码图片处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from PIL import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 处理像素\n",
    "def regularized(img):\n",
    "    width, height = img.size\n",
    "    for h in range(height):\n",
    "        for w in range(width):\n",
    "            pixel = img.getpixel((w, h))\n",
    "            if 42 <= pixel <= 43:\n",
    "                pixel = 0\n",
    "            else:\n",
    "                pixel = 255\n",
    "            img.putpixel((w, h), pixel)\n",
    "    return img"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 切割图像\n",
    "def cut(img):\n",
    "    width, height = img.size\n",
    "    new = img.crop((4, 1, width - 20, height - 5))\n",
    "    w, h = new.size\n",
    "    l = w // 4\n",
    "    return [new.crop((i * l, 0, (i + 1) * l, h)) for i in range(4)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 扫描获取像素点\n",
    "def scan(img):\n",
    "    temp = []\n",
    "    width, height = img.size\n",
    "    for h in range(height):\n",
    "        for w in range(width):\n",
    "            pixel = img.getpixel((w, h))\n",
    "            temp.append(pixel)\n",
    "    return temp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 二、卷积神经网络建模"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "import tensorflow as tf\n",
    "from tensorflow.keras import layers, optimizers, Sequential, utils\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv(\"captcha/learn2.csv\", header=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>0</th>\n",
       "      <th>1</th>\n",
       "      <th>2</th>\n",
       "      <th>3</th>\n",
       "      <th>4</th>\n",
       "      <th>5</th>\n",
       "      <th>6</th>\n",
       "      <th>7</th>\n",
       "      <th>8</th>\n",
       "      <th>9</th>\n",
       "      <th>...</th>\n",
       "      <th>243</th>\n",
       "      <th>244</th>\n",
       "      <th>245</th>\n",
       "      <th>246</th>\n",
       "      <th>247</th>\n",
       "      <th>248</th>\n",
       "      <th>249</th>\n",
       "      <th>250</th>\n",
       "      <th>251</th>\n",
       "      <th>252</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>...</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>27</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>...</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>17</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>...</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>...</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>19</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>...</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>255</td>\n",
       "      <td>18</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 253 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "   0    1    2    3    4    5    6    7    8    9    ...  243  244  245  246  \\\n",
       "0  255  255  255  255  255  255  255  255  255  255  ...  255  255  255  255   \n",
       "1  255  255  255  255  255  255  255  255  255  255  ...  255  255  255  255   \n",
       "2  255  255  255  255  255  255  255  255  255  255  ...  255  255  255  255   \n",
       "3  255  255  255  255  255  255  255  255  255  255  ...  255  255  255  255   \n",
       "4  255  255  255  255  255  255  255  255  255  255  ...  255  255  255  255   \n",
       "\n",
       "   247  248  249  250  251  252  \n",
       "0  255  255  255  255  255   27  \n",
       "1  255  255  255  255  255   17  \n",
       "2  255  255  255  255  255    0  \n",
       "3  255  255  255  255  255   19  \n",
       "4  255  255  255  255  255   18  \n",
       "\n",
       "[5 rows x 253 columns]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1200, 253)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train = data.iloc[:, :-1]\n",
    "Y_train = data.iloc[:, -1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "conv = lambda x: chr(x + 48 if 0 <= x <= 9 else x + 87)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "Y_label = Y_train.sort_values().apply(conv)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train = X_train / 255.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train = X_train.values.reshape(-1,21,12,1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "Y_train = utils.to_categorical(Y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1152x648 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.subplots(figsize=(16,9))\n",
    "sns.countplot(Y_label)\n",
    "plt.xlabel('Character', fontsize=16)\n",
    "plt.ylabel('Count', fontsize=16)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 缺少字符9，o和z，可能与g，0和2难以区分有关"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAALkAAAE1CAYAAAC7qEFLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAABF9JREFUeJzt3T1uU0EUgFEbkg0gaJAssgSqbMFrzhaoqKlAkWj4KWkQejSpUCTbQ/z83pdz6kk0iT5dubgab6dp2kDZi0tfAM5N5OSJnDyRkydy8kROnsjJEzl5Iifv6pTDr1+9nG521+e6C5zk8/3vzfeff7aHzp0U+c3uevPhbjd+K3hCt/v7o875uEKeyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5F1d+gJrt3/7/tJXSLj7+vFsv9skJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyZtlCHNnUO+dW2mNGtwnnvueI574paZKTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMlb7DNxcy9MrWHRatTo31ZZ7DLJyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZy8xW4hjio/9za3kf/J6P9/5Oc+TT+OOmeSkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5s2whzrnNBv8yyckTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5OWeieOylvjFuCY5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZNnC/GBL7l9Gkt83s8kJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyFruFuMQ39Vgnk5w8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyVvsFuJajGw9ruX9xMpGp0lOnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5Iicvt6C1huflKotPj5lz+ex2/+uocyY5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZOX20Jcg7mfiRvdelzLc3aHmOTkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp4txAdzvqE491ZgZZtwlElOnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJ80zcijz3L50dZZKTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXm2EP/TyIbf6DYhY0xy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IifPFuIFPPe3CedmkpMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvK20zQdf3i7/bbZbL6c7zpwknfTNL05dOikyGGNfFwhT+TkiZw8kZMncvJETp7IyRM5eSIn7y+AZ2P83gAHJwAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ax = plt.gca()\n",
    "plt.imshow(X_train[9,...,0])\n",
    "# plt.axis('off')\n",
    "# 去白\n",
    "plt.gca().xaxis.set_major_locator(plt.NullLocator()) \n",
    "plt.gca().yaxis.set_major_locator(plt.NullLocator()) \n",
    "plt.subplots_adjust(top=1,bottom=0,left=0,right=1,hspace=0,wspace=0) \n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Sequential((\n",
    "    layers.Conv2D(32, kernel_size=(5, 5), padding=\"same\", activation='relu', input_shape=(21, 12, 1)),\n",
    "    layers.Conv2D(32, kernel_size=(5, 5), padding=\"same\", activation='relu'),\n",
    "    layers.MaxPool2D(pool_size=(2, 2)),\n",
    "    layers.Dropout(0.25),\n",
    "    \n",
    "    layers.Conv2D(64, kernel_size=(3, 3), padding=\"same\", activation='relu'),\n",
    "    layers.Conv2D(64, kernel_size=(3, 3), padding=\"same\", activation='relu'),\n",
    "    layers.MaxPool2D(pool_size=(2, 2), strides=(2,2)),\n",
    "    layers.Dropout(0.25),\n",
    "    \n",
    "    layers.Flatten(),\n",
    "    layers.Dense(256, activation='relu'),\n",
    "    layers.Dropout(0.25),\n",
    "    layers.Dense(35, activation='softmax'),\n",
    "))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = optimizers.RMSprop(lr=1e-3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.compile(optimizer=optimizer, loss=\"categorical_crossentropy\", metrics=['accuracy'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 1200 samples\n",
      "Epoch 1/30\n",
      "1200/1200 [==============================] - 1s 525us/sample - loss: 0.0063 - accuracy: 0.9992\n",
      "Epoch 2/30\n",
      "1200/1200 [==============================] - 0s 277us/sample - loss: 0.0089 - accuracy: 0.9958\n",
      "Epoch 3/30\n",
      "1200/1200 [==============================] - 0s 256us/sample - loss: 0.0022 - accuracy: 0.9983\n",
      "Epoch 4/30\n",
      "1200/1200 [==============================] - 0s 264us/sample - loss: 0.0039 - accuracy: 0.9983\n",
      "Epoch 5/30\n",
      "1200/1200 [==============================] - 0s 264us/sample - loss: 0.0124 - accuracy: 0.9975\n",
      "Epoch 6/30\n",
      "1200/1200 [==============================] - 0s 272us/sample - loss: 0.0012 - accuracy: 0.9992\n",
      "Epoch 7/30\n",
      "1200/1200 [==============================] - 0s 274us/sample - loss: 0.0103 - accuracy: 0.9975\n",
      "Epoch 8/30\n",
      "1200/1200 [==============================] - 0s 269us/sample - loss: 0.0102 - accuracy: 0.9992\n",
      "Epoch 9/30\n",
      "1200/1200 [==============================] - 0s 274us/sample - loss: 0.0270 - accuracy: 0.9942\n",
      "Epoch 10/30\n",
      "1200/1200 [==============================] - 0s 296us/sample - loss: 0.0202 - accuracy: 0.9950\n",
      "Epoch 11/30\n",
      "1200/1200 [==============================] - 0s 268us/sample - loss: 0.0127 - accuracy: 0.9958\n",
      "Epoch 12/30\n",
      "1200/1200 [==============================] - 0s 273us/sample - loss: 1.3512e-04 - accuracy: 1.0000\n",
      "Epoch 13/30\n",
      "1200/1200 [==============================] - 0s 275us/sample - loss: 0.0076 - accuracy: 0.9975\n",
      "Epoch 14/30\n",
      "1200/1200 [==============================] - 0s 270us/sample - loss: 0.0171 - accuracy: 0.9942\n",
      "Epoch 15/30\n",
      "1200/1200 [==============================] - 0s 269us/sample - loss: 0.0015 - accuracy: 0.9992\n",
      "Epoch 16/30\n",
      "1200/1200 [==============================] - 0s 269us/sample - loss: 6.6047e-05 - accuracy: 1.0000\n",
      "Epoch 17/30\n",
      "1200/1200 [==============================] - 0s 260us/sample - loss: 0.0139 - accuracy: 0.9975\n",
      "Epoch 18/30\n",
      "1200/1200 [==============================] - 0s 258us/sample - loss: 0.0032 - accuracy: 0.9975\n",
      "Epoch 19/30\n",
      "1200/1200 [==============================] - 0s 268us/sample - loss: 0.0014 - accuracy: 0.9992\n",
      "Epoch 20/30\n",
      "1200/1200 [==============================] - 0s 255us/sample - loss: 0.0244 - accuracy: 0.9950\n",
      "Epoch 21/30\n",
      "1200/1200 [==============================] - 0s 265us/sample - loss: 0.0303 - accuracy: 0.9942\n",
      "Epoch 22/30\n",
      "1200/1200 [==============================] - 0s 279us/sample - loss: 0.0113 - accuracy: 0.9967\n",
      "Epoch 23/30\n",
      "1200/1200 [==============================] - 0s 272us/sample - loss: 0.0380 - accuracy: 0.9942\n",
      "Epoch 24/30\n",
      "1200/1200 [==============================] - 0s 269us/sample - loss: 0.0219 - accuracy: 0.9942\n",
      "Epoch 25/30\n",
      "1200/1200 [==============================] - 0s 270us/sample - loss: 0.0114 - accuracy: 0.9975\n",
      "Epoch 26/30\n",
      "1200/1200 [==============================] - 0s 276us/sample - loss: 0.0137 - accuracy: 0.9975\n",
      "Epoch 27/30\n",
      "1200/1200 [==============================] - 0s 274us/sample - loss: 0.0150 - accuracy: 0.9967\n",
      "Epoch 28/30\n",
      "1200/1200 [==============================] - 0s 277us/sample - loss: 0.0189 - accuracy: 0.9983\n",
      "Epoch 29/30\n",
      "1200/1200 [==============================] - 0s 259us/sample - loss: 0.0142 - accuracy: 0.9975\n",
      "Epoch 30/30\n",
      "1200/1200 [==============================] - 0s 263us/sample - loss: 0.0045 - accuracy: 0.9983\n"
     ]
    }
   ],
   "source": [
    "epochs = 30\n",
    "cap = model.fit(X_train, Y_train, epochs=epochs, batch_size=30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = pd.Series(list(map(conv, model.predict_classes(X_train))))\n",
    "m = pd.Series(list(map(conv, data.iloc[:, -1])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=28351, shape=(0, 1), dtype=int64, numpy=array([], shape=(0, 1), dtype=int64)>"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tf.where((n == m)==False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(n==m).all()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 说明识别完全正确"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 保存模型\n",
    "# model.save('model')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 三、预测新验证码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "from PIL import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "captcha = 'captcha/unknown/0.png'\n",
    "imgs = cut(regularized(Image.open(captcha)))\n",
    "data = [scan(img) for img in imgs]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = pd.DataFrame(data) / 255.0\n",
    "x = x.values.reshape(-1,21,12,1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAALkAAAE1CAYAAAC7qEFLAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAABA9JREFUeJzt3LGKU1EUQNEXzTSWos1AcCpLmcpfyDfnF6yCpZUykEaxnEbkWdkJyctM8szOWvUNuYTNIcXhLsZxHKDsxdwXgFMTOXkiJ0/k5ImcPJGTJ3LyRE6eyMlbTjn85vXL8W51c6q7wCRfH34NP37+Xuw7Nynyu9XN8GmzOv5W8Iw+rh8OOufvCnkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcvEkLWlym9e39UZ/b7LbPfJN5mOTkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvImLWh9+fzq6GWfqSrLQf9yrt/wr/JveQiTnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJm7SF+P7D47DZXPdGG5fHJCdP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8s7yFuK1v8XHvExy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IifPW4jkmeTkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETt5y7gtcuvXt/dm+a7Pbnu27Skxy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IifPFuITHbMZeM7NRUxyroDIyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5eSInT+TkiZw8kZMncvJETp7IyRM5ed5CfCLvGv7/THLyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ0/k5ImcPJGTJ3LyRE6eyMkTOXkiJ2859wUu3Wa3nfsK7GGSkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJEzl5IidP5OSJnDyRkydy8kROnsjJW4zjePjhxeL7MAzfTncdmOTdOI5v9x2aFDlcIn9XyBM5eSInT+TkiZw8kZMncvJETp7IyfsDHO06O8W4UwMAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "ax = plt.gca()\n",
    "plt.imshow(x[3,...,0])\n",
    "plt.gca().xaxis.set_major_locator(plt.NullLocator()) \n",
    "plt.gca().yaxis.set_major_locator(plt.NullLocator()) \n",
    "plt.subplots_adjust(top=1,bottom=0,left=0,right=1,hspace=0,wspace=0) \n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['d', 'e', '5', 'l']"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 输出预测值\n",
    "list(map(conv, model.predict_classes(x)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 批量预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = pd.read_csv(\"captcha/learn3.csv\", header=None)\n",
    "X = pd.DataFrame(X) / 255.0\n",
    "X = X.values.reshape(-1,21,12,1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 输出预测值\n",
    "lst = list(map(conv, model.predict_classes(X)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [],
   "source": [
    "res = np.array(lst).reshape(-1, 4).tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[['d', 'e', '5', 'l'],\n",
       " ['n', '2', 'i', 'b'],\n",
       " ['e', 'x', '1', 'x'],\n",
       " ['0', '8', '2', 's'],\n",
       " ['0', '8', '2', 's']]"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open(\"captcha/labels\", \"w\") as fp:\n",
    "    for i in res:\n",
    "        fp.write(''.join(i) + '\\n')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
